import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font, colorchooser
from tkinter import simpledialog
import os
import json
import shutil
from collections import deque
from datetime import datetime, timedelta
import hashlib
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os
import base64
import tkfontchooser

class EnhancedNotepad:
    def __init__(self, root):
        self.root = root
        self.root.title("增强版记事本")
        self.root.geometry("1000x700")
        
        # 当前文件路径
        self.current_file = None
        # 文本是否修改标志
        self.text_modified = False
        
        # 最近打开文件列表（最大10个）
        self.recent_files = deque(maxlen=10)
        
        # 剪贴板历史（最大10个）
        self.clipboard_history = deque(maxlen=10)
        
        # 撤销/重做历史
        self.undo_stack = []
        self.redo_stack = []
        self.max_undo_steps = 100
        
        # 查找相关变量
        self.find_str = tk.StringVar()
        self.replace_str = tk.StringVar()
        self.case_sensitive = tk.BooleanVar(value=False)
        self.search_results = []
        self.current_search_index = -1
        
        # 配置文件路径
        self.config_file = os.path.join(os.path.expanduser("~"), ".notepad_config.json")
        
        # 笔记分类体系
        self.folder_structure = {"folders": {}, "files": []}
        self.current_folder_path = ""  # 当前选中的文件夹路径
        
        # 安全设置
        self.encryption_key = None
        self.secure_notes = {}  # 存储加密笔记的元数据
        
        # 加载配置
        self.load_config()
        
        self.setup_ui()
        
        # 绑定事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.text_area.bind("<Key>", self.on_text_change)
        self.text_area.bind("<<Modified>>", self.on_text_modified)
        self.text_area.bind("<<Selection>>", self.on_selection_change)
        
        # 绑定Alt键用于列选模式
        self.text_area.bind("<Alt-Button-1>", self.start_column_select)
        self.text_area.bind("<Alt-B1-Motion>", self.on_column_select)
        self.text_area.bind("<ButtonRelease-1>", self.end_column_select)
        
        # 初始化字体
        self.current_font = font.Font(family="宋体", size=10)
        self.text_area.configure(font=self.current_font)
        
        # 是否处于列选模式
        self.column_select_mode = False
        self.column_select_start = None
        
        # 初始化文件夹结构
        self.refresh_folder_tree()
        
        # 设置默认存储路径
        self.default_storage_path = os.path.join(os.path.expanduser("~"), "NotepadNotes")
        if not os.path.exists(self.default_storage_path):
            os.makedirs(self.default_storage_path)
        
        # 启动自动备份检查
        self.check_auto_backup()
    
    def load_config(self):
        """加载配置文件"""
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    self.recent_files = deque(config.get('recent_files', []), maxlen=10)
                    self.max_undo_steps = config.get('max_undo_steps', 100)
                    self.folder_structure = config.get('folder_structure', {})
                    self.default_storage_path = config.get('default_storage_path', 
                                                         os.path.join(os.path.expanduser("~"), "NotepadNotes"))
                    self.backup_settings = config.get('backup_settings', {'enabled': False, 'frequency': 'daily'})
                    self.secure_notes = config.get('secure_notes', {})
            except:
                # 如果配置文件损坏，使用默认值
                self.recent_files = deque(maxlen=10)
                self.max_undo_steps = 100
                self.folder_structure = {}
                self.default_storage_path = os.path.join(os.path.expanduser("~"), "NotepadNotes")
                self.backup_settings = {'enabled': False, 'frequency': 'daily'}
                self.secure_notes = {}
            if not isinstance(self.folder_structure, dict) or \
               "folders" not in self.folder_structure or "files" not in self.folder_structure:
               self.folder_structure = {"folders": {}, "files": []}
    
    def save_config(self):
        """保存配置文件"""
        config = {
            'recent_files': list(self.recent_files),
            'max_undo_steps': self.max_undo_steps,
            'folder_structure': self.folder_structure,
            'default_storage_path': self.default_storage_path,
            'backup_settings': self.backup_settings,
            'secure_notes': self.secure_notes
        }
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
        except:
            pass  # 忽略保存配置时的错误
    
    def add_to_recent_files(self, file_path):
        """添加文件到最近打开列表"""
        if file_path in self.recent_files:
            self.recent_files.remove(file_path)
        self.recent_files.appendleft(file_path)
        self.save_config()
        self.update_recent_files_menu()
    
    def update_recent_files_menu(self):
        """更新最近打开文件菜单"""
        # 安全删除旧的"最近打开"菜单项
        try:
            self.file_menu.delete("最近打开")
        except tk.TclError:
            pass  # 不存在则忽略

        # 如果有最近文件，添加新菜单
        if self.recent_files:
            recent_menu = tk.Menu(self.file_menu, tearoff=0)
            
            for i, file_path in enumerate(self.recent_files):
                display_name = f"{i+1}. {os.path.basename(file_path)}"
                recent_menu.add_command(
                    label=display_name,
                    command=lambda path=file_path: self.open_recent_file(path)
                )
            
            recent_menu.add_separator()
            recent_menu.add_command(label="清除列表", command=self.clear_recent_files)

            self.file_menu.insert_cascade(2, menu=recent_menu, label="最近打开")
    
    def open_recent_file(self, file_path):
        """打开最近文件"""
        if not os.path.exists(file_path):
            messagebox.showwarning("文件不存在", f"文件不存在或已被移动:\n{file_path}")
            self.recent_files.remove(file_path)
            self.save_config()
            self.update_recent_files_menu()
            return
        
        if self.check_save():
            try:
                # 检查是否是加密文件
                if file_path in self.secure_notes:
                    if not self.decrypt_file(file_path):
                        return
                else:
                    with open(file_path, 'r', encoding='utf-8') as file:
                        content = file.read()
                
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(1.0, content)
                self.current_file = file_path
                self.text_modified = False
                self.update_title()
                self.add_to_recent_files(file_path)
                self.status_bar.config(text=f"已打开: {file_path}")
            except Exception as e:
                messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")
    
    def clear_recent_files(self):
        """清除最近文件列表"""
        self.recent_files.clear()
        self.save_config()
        self.update_recent_files_menu()
    
    def setup_ui(self):
        # 创建主分割窗口
        self.paned_window = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        self.paned_window.pack(fill=tk.BOTH, expand=True)
        
        # 左侧文件夹框架
        self.left_frame = ttk.Frame(self.paned_window, width=200)
        self.paned_window.add(self.left_frame, weight=1)
        
        # # 文件夹树
        # self.folder_tree = ttk.Treeview(self.left_frame, show='tree')
        # self.folder_tree.heading('#0', text='文件夹', anchor='w')
        # self.folder_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 文件夹树 - 修改为显示文件和文件夹
        self.folder_tree = ttk.Treeview(self.left_frame)
        # 定义列 ('type') 来区分文件夹和文件
        self.folder_tree["columns"] = ("type",)
        self.folder_tree.column("#0", width=150, minwidth=100, stretch=tk.YES)
        self.folder_tree.column("type", width=50, minwidth=50, stretch=tk.NO)
        self.folder_tree.heading("#0", text="名称", anchor='w')
        self.folder_tree.heading("type", text="类型", anchor='w')

        self.folder_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 文件夹操作按钮框架
        self.folder_btn_frame = ttk.Frame(self.left_frame)
        self.folder_btn_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(self.folder_btn_frame, text="新建文件夹", 
                  command=self.create_new_folder).pack(side=tk.LEFT, fill=tk.X, expand=True)
        ttk.Button(self.folder_btn_frame, text="删除", 
                  command=self.delete_folder).pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # 右侧主文本区域框架
        self.right_frame = ttk.Frame(self.paned_window)
        self.paned_window.add(self.right_frame, weight=3)
        
        # 创建菜单栏
        self.menu_bar = tk.Menu(self.root)
        self.root.config(menu=self.menu_bar)
        
        # 文件菜单
        self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="文件", menu=self.file_menu)
        
        # 新建子菜单
        self.new_menu = tk.Menu(self.file_menu, tearoff=0)
        self.file_menu.add_cascade(label="新建", menu=self.new_menu)
        self.new_menu.add_command(label="空白笔记", command=self.new_blank_file, accelerator="Ctrl+N")
        self.new_menu.add_command(label="模板笔记", command=self.new_template_file)
        self.new_menu.add_command(label="安全笔记", command=self.new_secure_note)
        
        self.file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        
        # 更新最近文件菜单
        self.update_recent_files_menu()
        
        self.file_menu.add_separator()
        self.file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        self.file_menu.add_command(label="另存为", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        
        # 存储设置子菜单
        self.storage_menu = tk.Menu(self.file_menu, tearoff=0)
        self.file_menu.add_cascade(label="存储设置", menu=self.storage_menu)
        self.storage_menu.add_command(label="设置存储路径", command=self.set_storage_path)
        self.storage_menu.add_command(label="备份设置", command=self.backup_settings_dialog)
        self.storage_menu.add_command(label="立即备份", command=self.manual_backup)
        
        self.file_menu.add_separator()
        self.file_menu.add_command(label="退出", command=self.on_closing)
        
        # 编辑菜单
        self.edit_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="编辑", menu=self.edit_menu)
        
        self.edit_menu.add_command(label="撤销", command=self.undo, accelerator="Ctrl+Z")
        self.edit_menu.add_command(label="重做", command=self.redo, accelerator="Ctrl+Y")
        self.edit_menu.add_separator()
        self.edit_menu.add_command(label="剪切", command=self.cut, accelerator="Ctrl+X")
        self.edit_menu.add_command(label="复制", command=self.copy, accelerator="Ctrl+C")
        self.edit_menu.add_command(label="粘贴", command=self.paste, accelerator="Ctrl+V")
        self.edit_menu.add_command(label="删除", command=self.delete, accelerator="Del")
        self.edit_menu.add_separator()
        self.edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
        self.edit_menu.add_command(label="查找", command=self.show_find_dialog, accelerator="Ctrl+F")
        self.edit_menu.add_command(label="替换", command=self.show_replace_dialog, accelerator="Ctrl+H")
        self.edit_menu.add_separator()
        
        # 剪贴板历史子菜单
        self.clipboard_menu = tk.Menu(self.edit_menu, tearoff=0)
        self.edit_menu.add_cascade(label="剪贴板历史", menu=self.clipboard_menu)
        self.update_clipboard_menu()
        
        # 格式菜单
        self.format_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="格式", menu=self.format_menu)
        
        self.format_menu.add_command(label="字体", command=self.choose_font)
        self.format_menu.add_command(label="颜色", command=self.choose_color)
        self.format_menu.add_separator()
        self.format_menu.add_command(label="粗体", command=self.toggle_bold, accelerator="Ctrl+B")
        self.format_menu.add_command(label="斜体", command=self.toggle_italic, accelerator="Ctrl+I")
        self.format_menu.add_command(label="下划线", command=self.toggle_underline, accelerator="Ctrl+U")
        self.format_menu.add_command(label="删除线", command=self.toggle_strikethrough)
        
        # 笔记菜单
        self.note_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="笔记", menu=self.note_menu)
        
        self.note_menu.add_command(label="新建文件夹", command=self.create_new_folder, accelerator="Ctrl+Shift+N")
        self.note_menu.add_command(label="重命名文件夹", command=self.rename_folder)
        self.note_menu.add_command(label="删除文件夹", command=self.delete_folder)
        self.note_menu.add_separator()
        
        # 排序子菜单
        self.sort_menu = tk.Menu(self.note_menu, tearoff=0)
        self.note_menu.add_cascade(label="排序方式", menu=self.sort_menu)
        self.sort_menu.add_command(label="按创建时间(新→旧)", command=lambda: self.sort_notes('created_new'))
        self.sort_menu.add_command(label="按创建时间(旧→新)", command=lambda: self.sort_notes('created_old'))
        self.sort_menu.add_command(label="按修改时间", command=lambda: self.sort_notes('modified'))
        self.sort_menu.add_command(label="按标题", command=lambda: self.sort_notes('title'))
        self.sort_menu.add_command(label="自定义排序", command=lambda: self.sort_notes('custom'))
        
        self.note_menu.add_separator()
        self.note_menu.add_command(label="批量操作", command=self.batch_operations)
        self.note_menu.add_command(label="回收站", command=self.show_recycle_bin)
        
        # 安全菜单
        self.security_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="安全", menu=self.security_menu)
        self.security_menu.add_command(label="加密当前笔记", command=self.encrypt_current_note)
        self.security_menu.add_command(label="解密当前笔记", command=self.decrypt_current_note)
        self.security_menu.add_command(label="设置加密密码", command=self.set_encryption_password)
        
        # 绑定快捷键
        self.root.bind('<Control-n>', lambda event: self.new_blank_file())
        self.root.bind('<Control-o>', lambda event: self.open_file())
        self.root.bind('<Control-s>', lambda event: self.save_file())
        self.root.bind('<Control-S>', lambda event: self.save_as_file())
        self.root.bind('<Control-z>', lambda event: self.undo())
        self.root.bind('<Control-y>', lambda event: self.redo())
        self.root.bind('<Control-x>', lambda event: self.cut())
        self.root.bind('<Control-c>', lambda event: self.copy())
        self.root.bind('<Control-v>', lambda event: self.paste())
        self.root.bind('<Control-a>', lambda event: self.select_all())
        self.root.bind('<Control-f>', lambda event: self.show_find_dialog())
        self.root.bind('<Control-h>', lambda event: self.show_replace_dialog())
        self.root.bind('<Control-b>', lambda event: self.toggle_bold())
        self.root.bind('<Control-i>', lambda event: self.toggle_italic())
        self.root.bind('<Control-u>', lambda event: self.toggle_underline())
        self.root.bind('<Control-N>', lambda event: self.create_new_folder())
        
        # 文本区域和工具栏
        self.text_toolbar = ttk.Frame(self.right_frame)
        self.text_toolbar.pack(fill=tk.X)
        
        # 排序按钮
        ttk.Button(self.text_toolbar, text="排序", command=self.show_sort_options).pack(side=tk.LEFT, padx=2)
        
        # 文本区域
        self.text_frame = ttk.Frame(self.right_frame)
        self.text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.text_area = tk.Text(self.text_frame, wrap="word", undo=True)
        self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.text_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.text_area.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.text_area.yview)
        
        # 状态栏
        self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 查找替换对话框框架（初始隐藏）
        self.find_replace_frame = ttk.Frame(self.root)
        
        ttk.Label(self.find_replace_frame, text="查找:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.find_entry = ttk.Entry(self.find_replace_frame, textvariable=self.find_str, width=20)
        self.find_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        
        ttk.Label(self.find_replace_frame, text="替换为:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        self.replace_entry = ttk.Entry(self.find_replace_frame, textvariable=self.replace_str, width=20)
        self.replace_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)
        
        ttk.Checkbutton(self.find_replace_frame, text="区分大小写", variable=self.case_sensitive).grid(row=0, column=2, padx=5, pady=5)
        
        ttk.Button(self.find_replace_frame, text="查找下一个", command=self.find_next).grid(row=0, column=3, padx=5, pady=5)
        ttk.Button(self.find_replace_frame, text="查找全部", command=self.find_all).grid(row=0, column=4, padx=5, pady=5)
        ttk.Button(self.find_replace_frame, text="替换", command=self.replace).grid(row=1, column=3, padx=5, pady=5)
        ttk.Button(self.find_replace_frame, text="替换全部", command=self.replace_all).grid(row=1, column=4, padx=5, pady=5)
        ttk.Button(self.find_replace_frame, text="关闭", command=self.hide_find_replace).grid(row=1, column=5, padx=5, pady=5)
        
        # 绑定文件夹树事件
        self.folder_tree.bind('<<TreeviewSelect>>', self.on_folder_select)
        self.folder_tree.bind('<Double-1>', self.on_item_double_click)  # 双击打开文件
        self.folder_tree.bind('<Button-3>', self.show_folder_context_menu)

    def refresh_folder_tree(self):
        """刷新文件夹树显示"""
        # 清空现有项目
        print("folder_structure:", self.folder_structure)
        for item in self.folder_tree.get_children():
            self.folder_tree.delete(item)

        def add_items_to_tree(parent_node_id, folder_data, current_path):
            """
            递归地将文件夹和文件添加到 Treeview。
            :param parent_node_id: Treeview 中父节点的 ID ('' 表示根节点)
            :param folder_data: 当前文件夹的数据 (dict)
            :param current_path: 当前文件夹在结构中的路径
            """
            # 添加子文件夹
            for folder_name, folder_info in folder_data.get('folders', {}).items():
                folder_full_path = os.path.join(current_path, folder_name) if current_path else folder_name
                folder_id = self.folder_tree.insert(parent_node_id, 'end', text=folder_name,
                                                    values=('📁',),  # 使用 emoji 或文本表示文件夹
                                                    tags=('folder', folder_full_path))  # tags 用于存储额外信息
                # 递归添加子文件夹的内容
                add_items_to_tree(folder_id, folder_info, folder_full_path)

            # 添加文件
            for filename in folder_data.get('files', []):
                file_id = self.folder_tree.insert(parent_node_id, 'end', text=filename,
                                                  values=('📄',),  # 使用 emoji 或文本表示文件
                                                  tags=('file', os.path.join(current_path,
                                                                             filename) if current_path else filename))

        # 从根节点开始添加
        add_items_to_tree('', self.folder_structure, '')

    def get_folder_node(self, path):
        """
        根据路径获取文件夹节点数据。
        :param path: 文件夹路径 (例如 'Folder1/SubFolderA')
        :return: 对应的文件夹数据字典，如果路径无效则返回 None
        """
        if not path:
            return self.folder_structure
        parts = path.split(os.sep) # 使用 os.sep 以兼容不同系统
        node = self.folder_structure
        for part in parts:
            if 'folders' in node and part in node['folders']:
                node = node['folders'][part]
            else:
                return None # 路径不存在
        return node

    def get_parent_folder_node(self, path):
        """
        根据路径获取其父文件夹节点数据。
        :param path: 文件或文件夹路径
        :return: 父文件夹数据字典和当前项名称，如果路径无效或为根则返回 (None, None)
        """
        if not path:
            return None, None
        parts = path.split(os.sep)
        if len(parts) == 1:
            return self.folder_structure, parts[0] # 父节点是根
        parent_path = os.sep.join(parts[:-1])
        parent_node = self.get_folder_node(parent_path)
        current_item_name = parts[-1]
        return parent_node, current_item_name

    def create_new_folder(self):
        """创建新文件夹（可选择文件系统位置）"""
        # 弹出对话框获取文件夹名称
        folder_name = simpledialog.askstring("新建文件夹", "请输入文件夹名称:")
        if not folder_name:
            return  # 用户取消或未输入

        # 让用户选择文件夹存放的父目录
        parent_dir = filedialog.askdirectory(title="选择文件夹存放位置", initialdir=self.default_storage_path)
        if not parent_dir:
            return  # 用户取消选择

        # 检查同级目录下是否已存在同名文件夹
        fs_folder_path = os.path.join(parent_dir, folder_name)
        if os.path.exists(fs_folder_path):
            messagebox.showerror("错误", "同级目录下已存在同名文件夹!")
            return

        try:
            # 在文件系统中创建真实文件夹
            os.makedirs(fs_folder_path, exist_ok=False)

            # 计算相对路径（相对于默认存储路径）
            rel_path = os.path.relpath(fs_folder_path, self.default_storage_path)
            rel_path_parts = rel_path.split(os.sep)
            # 更新程序内部结构
            node = self.folder_structure
            for part in rel_path_parts[:-1]:
                node = node.setdefault('folders', {}).setdefault(part, {'folders': {}, 'files': []})
            if 'folders' not in node:
                node['folders'] = {}
            node['folders'][folder_name] = {'folders': {}, 'files': []}

            self.save_config()  # 保存结构到配置
            self.refresh_folder_tree()  # 刷新UI
            self.status_bar.config(text=f"文件夹 '{folder_name}' 已创建于: {fs_folder_path}")
        except Exception as e:
            messagebox.showerror("错误", f"无法创建文件夹: {str(e)}")
    
    def create_sub_folder(self):
        """创建子文件夹"""
        if not self.current_folder_path:
            messagebox.showwarning("警告", "请先选择一个文件夹!")
            return
        
        folder_name = simpledialog.askstring("新建子文件夹", "请输入子文件夹名称:")
        if folder_name:
            # 在当前选中的文件夹下创建子文件夹
            current_node = self.get_folder_node(self.current_folder_path)
            if folder_name in current_node:
                messagebox.showerror("错误", "子文件夹已存在!")
                return
            
            current_node[folder_name] = {}
            self.save_config()
            self.refresh_folder_tree()
    
    def rename_folder(self):
        """重命名文件夹"""
        if not self.current_folder_path:
            messagebox.showwarning("警告", "请先选择一个文件夹!")
            return

        old_name = os.path.basename(self.current_folder_path)
        new_name = simpledialog.askstring("重命名文件夹", "请输入新名称:", initialvalue=old_name)
        if not new_name or new_name == old_name:
            return # 用户取消、未输入或名称未变

        # 获取父节点
        parent_node, folder_name = self.get_parent_folder_node(self.current_folder_path)
        if parent_node is None or folder_name is None or 'folders' not in parent_node or folder_name not in parent_node['folders']:
             messagebox.showerror("错误", "无法找到要重命名的文件夹!")
             return

        # 检查新名称是否已存在
        if new_name in parent_node['folders']:
            messagebox.showerror("错误", "同级目录下已存在同名文件夹!")
            return

        # 确定文件系统中的旧路径和新路径
        old_fs_path = os.path.join(self.default_storage_path, self.current_folder_path)
        new_fs_path = os.path.join(os.path.dirname(old_fs_path), new_name)

        try:
            # 在文件系统中重命名
            if os.path.exists(old_fs_path):
                os.rename(old_fs_path, new_fs_path)

            # 在程序内部结构中重命名
            folder_data = parent_node['folders'].pop(folder_name) # 取出数据
            parent_node['folders'][new_name] = folder_data # 用新名称存入

            self.save_config()
            self.refresh_folder_tree()
            # 更新 current_folder_path (简单起见，这里只更新最后一级)
            self.current_folder_path = os.path.join(os.path.dirname(self.current_folder_path), new_name)
            self.status_bar.config(text=f"文件夹已重命名为: {new_name}")

        except Exception as e:
             messagebox.showerror("错误", f"重命名文件夹时出错: {str(e)}")
    
    def delete_folder(self):
        """删除文件夹"""
        if not self.current_folder_path:
            messagebox.showwarning("警告", "请先选择一个文件夹!")
            return

        # 确认删除
        if not messagebox.askyesno("确认删除", f"确定要删除文件夹 '{os.path.basename(self.current_folder_path)}' 及其所有内容吗？此操作不可恢复。"):
            return

        # 获取父节点和当前文件夹名
        parent_node, folder_name = self.get_parent_folder_node(self.current_folder_path)
        if parent_node is None or folder_name is None or 'folders' not in parent_node or folder_name not in parent_node['folders']:
            messagebox.showerror("错误", "无法找到要删除的文件夹!")
            return

        # 确定文件系统中的完整路径
        fs_folder_path = os.path.join(self.default_storage_path, self.current_folder_path)
        try:
            # 在文件系统中删除
            if os.path.exists(fs_folder_path):
                 shutil.rmtree(fs_folder_path) # 递归删除文件夹及其内容

            # 从程序内部结构中移除
            del parent_node['folders'][folder_name]

            self.save_config()
            self.refresh_folder_tree()
            self.current_folder_path = "" # 重置当前路径
            self.status_bar.config(text="文件夹已删除")
        except Exception as e:
            messagebox.showerror("错误", f"删除文件夹时出错: {str(e)}")

    def new_blank_file_in_folder(self, folder_path):
        """在指定文件夹中新建空白文件"""
        # 弹出对话框获取文件名
        file_name = simpledialog.askstring("新建笔记", "请输入笔记名称 (不含扩展名):")
        if not file_name:
            return  # 用户取消或未输入

        # 确保文件有 .md 扩展名
        if not file_name.endswith('.md'):
            file_name += '.md'

        # 确定文件系统中的完整路径
        full_fs_path = os.path.join(self.default_storage_path, folder_path, file_name)

        # 获取程序内部结构中的目标文件夹节点
        target_folder_node = self.get_folder_node(folder_path)
        if target_folder_node is None:
            messagebox.showerror("错误", "无法找到目标文件夹!")
            return

        # 检查文件是否已存在
        if file_name in target_folder_node.get('files', []):
            messagebox.showerror("错误", "文件已存在!")
            return

        try:
            # 确保文件系统中的目录存在
            os.makedirs(os.path.dirname(full_fs_path), exist_ok=True)
            # 在文件系统中创建空白文件
            with open(full_fs_path, 'w', encoding='utf-8') as f:
                f.write('# ' + os.path.splitext(file_name)[0] + '\n\n')  # 写入标题

            # 更新程序内部结构
            if 'files' not in target_folder_node:
                target_folder_node['files'] = []
            target_folder_node['files'].append(file_name)

            self.save_config()  # 保存配置
            self.refresh_folder_tree()  # 刷新 UI

            # 打开新创建的文件
            self.open_file_at_path(full_fs_path)

        except Exception as e:
            messagebox.showerror("错误", f"创建文件时出错: {str(e)}")

    def open_file_at_path(self, file_path):
        """打开指定路径的文件"""
        if self.check_save():  # 检查当前文件是否需要保存
            try:
                # 检查是否是加密文件 (这部分逻辑保持不变，但需要你实现 decrypt_file)
                # if file_path in self.secure_notes:
                #     if not self.decrypt_file(file_path):
                #         return
                # else:
                with open(file_path, 'r', encoding='utf-8') as file:
                    content = file.read()
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(1.0, content)
                self.current_file = file_path
                self.text_modified = False
                self.update_title()
                self.add_to_recent_files(file_path)
                self.status_bar.config(text=f"已打开: {file_path}")
            except Exception as e:
                messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")

    def on_folder_select(self, event):
        """文件夹/文件选择事件"""
        selected = self.folder_tree.selection()
        if selected:
            item = selected[0]
            item_tags = self.folder_tree.item(item, 'tags')
            if item_tags:
                item_type = item_tags[0]
                item_path = item_tags[1] if len(item_tags) > 1 else ""
                if item_type == 'folder':
                    self.current_folder_path = item_path
                    self.status_bar.config(text=f"当前文件夹: {self.current_folder_path}")
                elif item_type == 'file':
                    # 如果选中的是文件，可以更新状态栏，但不改变 current_folder_path
                    # 或者可以在这里直接打开文件？取决于交互设计
                    self.status_bar.config(text=f"选中文件: {item_path}")
                # else: # 可能是根节点等
                #     self.current_folder_path = ""
                #     self.status_bar.config(text="就绪")

    def on_item_double_click(self, event):
        """双击文件夹树项目"""
        selected = self.folder_tree.selection()
        if selected:
            item = selected[0]
            item_tags = self.folder_tree.item(item, 'tags')
            if item_tags:
                item_type = item_tags[0]
                item_path = item_tags[1] if len(item_tags) > 1 else ""
                if item_type == 'file':
                    full_fs_path = os.path.join(self.default_storage_path, item_path)
                    self.open_file_at_path(full_fs_path)
                # elif item_type == 'folder':
                #     # 双击文件夹可以展开/折叠，这是 Treeview 默认行为
                #     pass

    def show_folder_context_menu(self, event):
        """显示文件夹/文件右键菜单"""
        # 首先识别点击的项目
        item = self.folder_tree.identify_row(event.y)
        if item:
            self.folder_tree.selection_set(item)  # 选中被点击的项目
            item_tags = self.folder_tree.item(item, 'tags')
            if not item_tags:
                return
            item_type = item_tags[0]
            item_path = item_tags[1] if len(item_tags) > 1 else ""

            # 创建上下文菜单
            context_menu = tk.Menu(self.root, tearoff=0)

            if item_type == 'folder':
                # 针对文件夹的菜单
                context_menu.add_command(label="新建文件夹", command=lambda: self.create_new_folder())  # 在当前选中文件夹下创建
                context_menu.add_command(label="新建笔记", command=lambda: self.new_blank_file_in_folder(item_path))
                context_menu.add_separator()
                context_menu.add_command(label="重命名", command=self.rename_folder)
                context_menu.add_command(label="删除", command=self.delete_folder)
            elif item_type == 'file':
                # 针对文件的菜单
                context_menu.add_command(label="打开", command=lambda: self.on_item_double_click(None))  # 复用双击逻辑
                # context_menu.add_command(label="重命名", command=lambda: self.rename_file(item_path)) # 需要实现
                context_menu.add_command(label="删除", command=lambda: self.delete_file(item_path))  # 需要实现
                # context_menu.add_separator()
                # context_menu.add_command(label="移动到...", command=lambda: self.move_file(item_path)) # 需要实现

            # 显示菜单
            try:
                context_menu.post(event.x_root, event.y_root)
            finally:
                context_menu.grab_release()

    def delete_file(self, file_relative_path):
        """删除一个文件"""
        if not file_relative_path:
            messagebox.showwarning("警告", "无效的文件路径!")
            return

        if not messagebox.askyesno("确认删除",
                                   f"确定要删除文件 '{os.path.basename(file_relative_path)}' 吗？此操作不可恢复。"):
            return

        # 确定文件系统中的完整路径
        full_fs_path = os.path.join(self.default_storage_path, file_relative_path)

        # 获取父文件夹节点和文件名
        parent_folder_path = os.path.dirname(file_relative_path)
        filename = os.path.basename(file_relative_path)
        parent_node = self.get_folder_node(parent_folder_path)

        if parent_node is None or 'files' not in parent_node or filename not in parent_node['files']:
            messagebox.showerror("错误", "无法在内部结构中找到要删除的文件!")
            return

        try:
            # 在文件系统中删除
            if os.path.exists(full_fs_path):
                os.remove(full_fs_path)

            # 从程序内部结构中移除
            parent_node['files'].remove(filename)

            self.save_config()
            self.refresh_folder_tree()
            self.status_bar.config(text=f"文件已删除: {filename}")

            # 如果删除的是当前打开的文件
            if self.current_file and os.path.normpath(self.current_file) == os.path.normpath(full_fs_path):
                self.text_area.delete(1.0, tk.END)
                self.current_file = None
                self.text_modified = False
                self.update_title()
                self.status_bar.config(text="文件已删除")

        except Exception as e:
            messagebox.showerror("错误", f"删除文件时出错: {str(e)}")
    
    def add_to_recycle_bin(self, path, content, item_type):
        """添加到回收站"""
        # 这里实现回收站逻辑
        # 简化处理：在配置中记录删除的项目和删除时间
        if 'recycle_bin' not in self.__dict__:
            self.recycle_bin = {}
        
        self.recycle_bin[path] = {
            'content': content,
            'type': item_type,
            'deleted_time': datetime.now().isoformat()
        }
        self.save_config()
    
    def show_recycle_bin(self):
        """显示回收站"""
        # 创建回收站对话框
        recycle_window = tk.Toplevel(self.root)
        recycle_window.title("回收站")
        recycle_window.geometry("600x400")
        
        frame = ttk.Frame(recycle_window)
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 创建树形视图显示回收站内容
        columns = ('name', 'type', 'deleted_time')
        tree = ttk.Treeview(frame, columns=columns, show='headings')
        
        tree.heading('name', text='名称')
        tree.heading('type', text='类型')
        tree.heading('deleted_time', text='删除时间')
        
        tree.column('name', width=200)
        tree.column('type', width=100)
        tree.column('deleted_time', width=150)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscrollcommand=scrollbar.set)
        
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 添加按钮框架
        btn_frame = ttk.Frame(recycle_window)
        btn_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Button(btn_frame, text="恢复", command=lambda: self.restore_from_recycle_bin(tree)).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="永久删除", command=lambda: self.permanent_delete(tree)).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="清空回收站", command=self.empty_recycle_bin).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="关闭", command=recycle_window.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 填充回收站内容
        if hasattr(self, 'recycle_bin'):
            for path, item in self.recycle_bin.items():
                name = os.path.basename(path)
                tree.insert('', 'end', values=(name, item['type'], item['deleted_time']), tags=(path,))
    
    def restore_from_recycle_bin(self, tree):
        """从回收站恢复项目"""
        selected = tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个项目!")
            return
        
        for item in selected:
            path = tree.item(item, 'tags')[0]
            if path in self.recycle_bin:
                item_data = self.recycle_bin[path]
                # 恢复项目到原位置
                # 这里简化处理，实际需要更复杂的逻辑
                del self.recycle_bin[path]
                self.save_config()
                tree.delete(item)
    
    def permanent_delete(self, tree):
        """永久删除回收站中的项目"""
        selected = tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个项目!")
            return
        
        if messagebox.askyesno("确认删除", "确定要永久删除选中的项目吗？此操作不可恢复。"):
            for item in selected:
                path = tree.item(item, 'tags')[0]
                if path in self.recycle_bin:
                    del self.recycle_bin[path]
                    self.save_config()
                    tree.delete(item)
    
    def empty_recycle_bin(self):
        """清空回收站"""
        if hasattr(self, 'recycle_bin') and self.recycle_bin:
            if messagebox.askyesno("确认清空", "确定要清空回收站吗？此操作不可恢复。"):
                self.recycle_bin = {}
                self.save_config()
                messagebox.showinfo("成功", "回收站已清空!")
    
    def sort_notes(self, method):
        """按指定方式排序笔记"""
        # 这里实现排序逻辑
        # 在实际应用中，这可能会影响文件列表的显示顺序
        self.status_bar.config(text=f"已按 {method} 排序")
    
    def show_sort_options(self):
        """显示排序选项弹出菜单"""
        menu = tk.Menu(self.root, tearoff=0)
        menu.add_command(label="按创建时间(新→旧)", command=lambda: self.sort_notes('created_new'))
        menu.add_command(label="按创建时间(旧→新)", command=lambda: self.sort_notes('created_old'))
        menu.add_command(label="按修改时间", command=lambda: self.sort_notes('modified'))
        menu.add_command(label="按标题", command=lambda: self.sort_notes('title'))
        menu.add_command(label="自定义排序", command=lambda: self.sort_notes('custom'))
        
        # 在按钮下方显示菜单
        menu.post(self.text_toolbar.winfo_rootx(), 
                 self.text_toolbar.winfo_rooty() + self.text_toolbar.winfo_height())
    
    def batch_operations(self):
        """批量操作对话框"""
        # 创建批量操作对话框
        batch_window = tk.Toplevel(self.root)
        batch_window.title("批量操作")
        batch_window.geometry("400x300")
        
        # 这里实现批量操作界面
        # 包括选择多个文件，然后进行移动、删除、添加标签等操作
        
        ttk.Label(batch_window, text="批量操作功能").pack(pady=20)
        ttk.Button(batch_window, text="关闭", command=batch_window.destroy).pack(pady=10)
    
    def set_storage_path(self):
        """设置默认存储路径"""
        new_path = filedialog.askdirectory(title="选择笔记存储路径", initialdir=self.default_storage_path)
        if new_path:
            self.default_storage_path = new_path
            self.save_config()
            messagebox.showinfo("成功", f"存储路径已设置为: {new_path}")
    
    def backup_settings_dialog(self):
        """备份设置对话框"""
        backup_window = tk.Toplevel(self.root)
        backup_window.title("备份设置")
        backup_window.geometry("400x200")
        
        frame = ttk.Frame(backup_window, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 启用备份复选框
        backup_enabled = tk.BooleanVar(value=self.backup_settings['enabled'])
        ttk.Checkbutton(frame, text="启用自动备份", variable=backup_enabled).grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=5)
        
        ttk.Label(frame, text="备份频率:").grid(row=1, column=0, sticky=tk.W, pady=5)
        frequency = ttk.Combobox(frame, values=['daily', 'weekly'], state='readonly')
        frequency.set(self.backup_settings['frequency'])
        frequency.grid(row=1, column=1, sticky=tk.W, pady=5)
        
        ttk.Label(frame, text="备份路径:").grid(row=2, column=0, sticky=tk.W, pady=5)
        path_var = tk.StringVar(value=self.backup_settings.get('path', self.default_storage_path + '_backup'))
        ttk.Entry(frame, textvariable=path_var, width=30).grid(row=2, column=1, sticky=tk.W, pady=5)
        ttk.Button(frame, text="浏览", command=lambda: self.choose_backup_path(path_var)).grid(row=2, column=2, sticky=tk.W, pady=5)
        
        def save_backup_settings():
            self.backup_settings = {
                'enabled': backup_enabled.get(),
                'frequency': frequency.get(),
                'path': path_var.get()
            }
            self.save_config()
            backup_window.destroy()
            messagebox.showinfo("成功", "备份设置已保存")
        
        ttk.Button(frame, text="保存", command=save_backup_settings).grid(row=3, column=1, pady=10)
        ttk.Button(frame, text="取消", command=backup_window.destroy).grid(row=3, column=2, pady=10)
    
    def choose_backup_path(self, path_var):
        """选择备份路径"""
        path = filedialog.askdirectory(title="选择备份路径", initialdir=path_var.get())
        if path:
            path_var.set(path)
    
    def manual_backup(self):
        """手动立即备份"""
        if self.create_backup():
            messagebox.showinfo("成功", "备份已完成")
    
    def check_auto_backup(self):
        """检查是否需要自动备份"""
        if self.backup_settings.get('enabled', False):
            # 检查上次备份时间
            last_backup = self.backup_settings.get('last_backup')
            need_backup = False
            
            if not last_backup:
                need_backup = True
            else:
                last_backup_time = datetime.fromisoformat(last_backup)
                frequency = self.backup_settings.get('frequency', 'daily')
                
                if frequency == 'daily' and datetime.now() - last_backup_time > timedelta(days=1):
                    need_backup = True
                elif frequency == 'weekly' and datetime.now() - last_backup_time > timedelta(weeks=1):
                    need_backup = True
            
            if need_backup:
                self.create_backup()
        
        # 每天检查一次
        self.root.after(24 * 60 * 60 * 1000, self.check_auto_backup)
    
    def create_backup(self):
        """创建备份"""
        try:
            backup_path = self.backup_settings.get('path', self.default_storage_path + '_backup')
            if not os.path.exists(backup_path):
                os.makedirs(backup_path)
            
            # 创建带时间戳的备份文件夹
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_dir = os.path.join(backup_path, f"backup_{timestamp}")
            os.makedirs(backup_dir)
            
            # 复制所有文件
            if os.path.exists(self.default_storage_path):
                shutil.copytree(self.default_storage_path, os.path.join(backup_dir, "notes"))
            
            # 保存配置
            self.backup_settings['last_backup'] = datetime.now().isoformat()
            self.save_config()
            
            return True
        except Exception as e:
            messagebox.showerror("备份错误", f"创建备份时出错: {str(e)}")
            return False
    
    def set_encryption_password(self):
        """设置加密密码"""
        password = simpledialog.askstring("设置密码", "请输入加密密码:", show='*')
        if password:
            confirm = simpledialog.askstring("确认密码", "请再次输入密码确认:", show='*')
            if password == confirm:
                # 使用SHA256哈希密码作为加密密钥的基础
                self.encryption_key = hashlib.sha256(password.encode()).digest()
                messagebox.showinfo("成功", "密码已设置")
            else:
                messagebox.showerror("错误", "密码不匹配")

    def encrypt_current_note(self):
        """加密当前笔记"""
        if not self.current_file:
            messagebox.showwarning("警告", "请先打开或创建一个笔记!")
            return

        if not self.encryption_key:
            self.set_encryption_password()
            if not self.encryption_key:
                return  # 用户取消了密码设置

        try:
            # 读取文件内容
            with open(self.current_file, 'r', encoding='utf-8') as f:
                content = f.read()

            # 生成随机IV
            iv = os.urandom(16)

            # 创建加密器
            cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
            encryptor = cipher.encryptor()

            # 填充并加密数据
            padder = padding.PKCS7(128).padder()
            padded_data = padder.update(content.encode('utf-8')) + padder.finalize()
            encrypted_data = encryptor.update(padded_data) + encryptor.finalize()

            # 保存加密内容 (IV + 加密数据)
            combined = iv + encrypted_data
            encrypted_content = base64.b64encode(combined).decode('utf-8')

            with open(self.current_file, 'w', encoding='utf-8') as f:
                f.write(encrypted_content)

            # 记录为安全笔记
            self.secure_notes[self.current_file] = True
            self.save_config()

            messagebox.showinfo("成功", "笔记已加密")
        except Exception as e:
            messagebox.showerror("错误", f"加密失败: {str(e)}")

    def decrypt_current_note(self):
        """解密当前笔记"""
        if not self.current_file or self.current_file not in self.secure_notes:
            messagebox.showwarning("警告", "当前笔记未加密!")
            return

        if not self.encryption_key:
            password = simpledialog.askstring("输入密码", "请输入解密密码:", show='*')
            if password:
                # 使用SHA256哈希密码作为加密密钥
                self.encryption_key = hashlib.sha256(password.encode()).digest()
            else:
                return

        try:
            # 读取加密内容
            with open(self.current_file, 'r', encoding='utf-8') as f:
                encrypted_data = f.read()

            # 解码base64
            combined = base64.b64decode(encrypted_data)

            # 提取IV和加密数据
            iv = combined[:16]
            ciphertext = combined[16:]

            # 创建解密器
            cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
            decryptor = cipher.decryptor()

            # 解密数据
            padded_data = decryptor.update(ciphertext) + decryptor.finalize()

            # 去除填充
            unpadder = padding.PKCS7(128).unpadder()
            data = unpadder.update(padded_data) + unpadder.finalize()

            # 更新文本区域
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, data.decode('utf-8'))
            self.text_modified = False
            self.update_title()

            # 从安全笔记中移除
            if self.current_file in self.secure_notes:
                del self.secure_notes[self.current_file]
                self.save_config()

            messagebox.showinfo("成功", "笔记已解密")
        except Exception as e:
            messagebox.showerror("错误", f"解密失败: {str(e)}")
    def decrypt_file(self, file_path):
        """解密文件"""
        try:
            # 读取加密内容
            with open(file_path, 'r', encoding='utf-8') as f:
                encrypted_data = f.read()

            # 解码base64
            combined = base64.b64decode(encrypted_data)

            # 提取IV和加密数据
            iv = combined[:16]
            ciphertext = combined[16:]

            # 创建解密器
            cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
            decryptor = cipher.decryptor()

            # 解密数据
            padded_data = decryptor.update(ciphertext) + decryptor.finalize()

            # 去除填充
            unpadder = padding.PKCS7(128).unpadder()
            data = unpadder.update(padded_data) + unpadder.finalize()

            # 更新文本区域
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, data.decode('utf-8'))
            self.text_modified = False
            self.update_title()

            return True
        except Exception as e:
            messagebox.showerror("错误", f"解密失败: {str(e)}")
            return False
    
    def new_secure_note(self):
        """新建安全笔记"""
        if not self.encryption_key:
            self.set_encryption_password()
            if not self.encryption_key:
                return  # 用户取消了密码设置
        
        # 先创建普通笔记
        self.new_blank_file()
        
        # 然后立即加密
        if self.current_file:
            self.encrypt_current_note()
    
    def update_clipboard_menu(self):
        """更新剪贴板历史菜单"""
        self.clipboard_menu.delete(0, tk.END)
        
        if not self.clipboard_history:
            self.clipboard_menu.add_command(label="剪贴板为空", state=tk.DISABLED)
            return
            
        for i, content in enumerate(self.clipboard_history):
            # 显示前30个字符作为预览
            preview = content[:30] + "..." if len(content) > 30 else content
            self.clipboard_menu.add_command(
                label=f"{i+1}. {preview}", 
                command=lambda c=content: self.paste_from_history(c)
            )
        
        self.clipboard_menu.add_separator()
        self.clipboard_menu.add_command(label="清空剪贴板历史", command=self.clear_clipboard_history)
    
    def paste_from_history(self, content):
        """从剪贴板历史粘贴内容"""
        self.text_area.insert(tk.INSERT, content)
    
    def clear_clipboard_history(self):
        """清空剪贴板历史"""
        self.clipboard_history.clear()
        self.update_clipboard_menu()
    
    def add_to_clipboard_history(self, content):
        """添加内容到剪贴板历史"""
        if content.strip():  # 只添加非空内容
            if content in self.clipboard_history:
                self.clipboard_history.remove(content)
            self.clipboard_history.appendleft(content)
            self.update_clipboard_menu()
    
    def on_text_change(self, event=None):
        """文本内容改变时的回调"""
        self.text_modified = True
        self.update_title()
        
        # 记录撤销点
        if event and event.keysym not in ['Shift_L', 'Shift_R', 'Control_L', 'Control_R', 'Alt_L', 'Alt_R']:
            self.record_undo_point()
    
    def on_text_modified(self, event=None):
        """文本修改事件"""
        if self.text_area.edit_modified():
            self.text_modified = True
            self.update_title()
            self.text_area.edit_modified(False)
    
    def on_selection_change(self, event=None):
        """选择文本改变时的回调"""
        try:
            if self.text_area.tag_ranges(tk.SEL):
                start = self.text_area.index(tk.SEL_FIRST)
                end = self.text_area.index(tk.SEL_LAST)
                self.status_bar.config(text=f"已选择: {start} 到 {end}")
            else:
                cursor_pos = self.text_area.index(tk.INSERT)
                self.status_bar.config(text=f"位置: {cursor_pos}")
        except:
            pass
    
    def start_column_select(self, event):
        """开始列选模式"""
        self.column_select_mode = True
        self.column_select_start = (event.x, event.y)
        # 清除当前选择
        self.text_area.tag_remove(tk.SEL, "1.0", tk.END)
    
    def on_column_select(self, event):
        """列选模式中"""
        if not self.column_select_mode or not self.column_select_start:
            return
            
        start_x, start_y = self.column_select_start
        end_x, end_y = event.x, event.y
        
        # 获取起始和结束位置
        start_index = self.text_area.index(f"@{start_x},{start_y}")
        end_index = self.text_area.index(f"@{end_x},{end_y}")
        
        # 清除当前选择
        self.text_area.tag_remove(tk.SEL, "1.0", tk.END)
        
        # 计算列选区域
        start_line, start_char = map(int, start_index.split('.'))
        end_line, end_char = map(int, end_index.split('.'))
        
        # 确保起始字符小于结束字符
        if start_char > end_char:
            start_char, end_char = end_char, start_char
        
        # 选择多行中的相同列范围
        for line in range(min(start_line, end_line), max(start_line, end_line) + 1):
            sel_start = f"{line}.{start_char}"
            sel_end = f"{line}.{end_char}"
            self.text_area.tag_add(tk.SEL, sel_start, sel_end)
    
    def end_column_select(self, event):
        """结束列选模式"""
        self.column_select_mode = False
        self.column_select_start = None
    
    def update_title(self):
        """更新窗口标题"""
        if self.current_file:
            filename = os.path.basename(self.current_file)
        else:
            filename = "未命名"
        
        if self.text_modified:
            filename += "*"
        
        self.root.title(f"{filename} - 增强版记事本")
    
    def record_undo_point(self):
        """记录撤销点"""
        if len(self.undo_stack) >= self.max_undo_steps:
            self.undo_stack.pop(0)
        
        content = self.text_area.get(1.0, tk.END)
        self.undo_stack.append(content)
        self.redo_stack.clear()  # 新的操作清空重做栈
    
    def undo(self):
        """撤销操作"""
        if self.undo_stack:
            # 保存当前状态到重做栈
            current_content = self.text_area.get(1.0, tk.END)
            self.redo_stack.append(current_content)
            
            # 恢复上一个状态
            prev_content = self.undo_stack.pop()
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, prev_content)
            
            self.text_modified = True
            self.update_title()
    
    def redo(self):
        """重做操作"""
        if self.redo_stack:
            # 保存当前状态到撤销栈
            current_content = self.text_area.get(1.0, tk.END)
            self.undo_stack.append(current_content)
            
            # 恢复重做状态
            next_content = self.redo_stack.pop()
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, next_content)
            
            self.text_modified = True
            self.update_title()
    
    def cut(self):
        """剪切文本"""
        if self.text_area.tag_ranges(tk.SEL):
            selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.add_to_clipboard_history(selected_text)
            self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
    
    def copy(self):
        """复制文本"""
        if self.text_area.tag_ranges(tk.SEL):
            selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.add_to_clipboard_history(selected_text)
            self.root.clipboard_clear()
            self.root.clipboard_append(selected_text)
    
    def paste(self):
        """粘贴文本"""
        try:
            clipboard_content = self.root.clipboard_get()
            if clipboard_content:
                self.add_to_clipboard_history(clipboard_content)
                self.text_area.insert(tk.INSERT, clipboard_content)
        except:
            pass  # 剪贴板可能为空或包含不可粘贴的内容
    
    def delete(self):
        """删除选中文本"""
        if self.text_area.tag_ranges(tk.SEL):
            self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
    
    def select_all(self):
        """全选文本"""
        self.text_area.tag_add(tk.SEL, "1.0", tk.END)
        self.text_area.mark_set(tk.INSERT, "1.0")
        self.text_area.see(tk.INSERT)
    
    def show_find_dialog(self):
        """显示查找对话框"""
        self.find_replace_frame.pack(side=tk.BOTTOM, fill=tk.X)
        self.find_entry.focus_set()
    
    def show_replace_dialog(self):
        """显示替换对话框"""
        self.find_replace_frame.pack(side=tk.BOTTOM, fill=tk.X)
        self.find_entry.focus_set()
    
    def hide_find_replace(self):
        """隐藏查找替换对话框"""
        self.find_replace_frame.pack_forget()
    
    def find_next(self):
        """查找下一个匹配项"""
        find_text = self.find_str.get()
        if not find_text:
            return
            
        start_pos = self.text_area.index(tk.INSERT)
        content = self.text_area.get(1.0, tk.END)
        
        # 根据是否区分大小写进行查找
        if not self.case_sensitive.get():
            find_text = find_text.lower()
            content_lower = content.lower()
            pos = content_lower.find(find_text, self.text_area.index(start_pos))
        else:
            pos = content.find(find_text, self.text_area.index(start_pos))
        
        if pos != -1:
            line_start = content.rfind('\n', 0, pos) + 1
            line_end = content.find('\n', pos)
            if line_end == -1:
                line_end = len(content)
            
            line = content.count('\n', 0, pos) + 1
            col_start = pos - line_start
            col_end = col_start + len(find_text)
            
            start_index = f"{line}.{col_start}"
            end_index = f"{line}.{col_end}"
            
            self.text_area.tag_remove(tk.SEL, 1.0, tk.END)
            self.text_area.tag_add(tk.SEL, start_index, end_index)
            self.text_area.mark_set(tk.INSERT, end_index)
            self.text_area.see(tk.INSERT)
        else:
            messagebox.showinfo("查找", "已到达文档末尾")
    
    def find_all(self):
        """查找所有匹配项"""
        find_text = self.find_str.get()
        if not find_text:
            return
            
        content = self.text_area.get(1.0, tk.END)
        self.search_results = []
        
        # 根据是否区分大小写进行查找
        if not self.case_sensitive.get():
            find_text = find_text.lower()
            content_lower = content.lower()
            pos = content_lower.find(find_text)
            while pos != -1:
                self.search_results.append(pos)
                pos = content_lower.find(find_text, pos + 1)
        else:
            pos = content.find(find_text)
            while pos != -1:
                self.search_results.append(pos)
                pos = content.find(find_text, pos + 1)
        
        if self.search_results:
            self.current_search_index = 0
            self.highlight_search_result(0)
            messagebox.showinfo("查找", f"找到 {len(self.search_results)} 个匹配项")
        else:
            messagebox.showinfo("查找", "未找到匹配项")
    
    def highlight_search_result(self, index):
        """高亮显示查找结果"""
        if not self.search_results or index < 0 or index >= len(self.search_results):
            return
            
        pos = self.search_results[index]
        content = self.text_area.get(1.0, tk.END)
        
        line_start = content.rfind('\n', 0, pos) + 1
        line_end = content.find('\n', pos)
        if line_end == -1:
            line_end = len(content)
        
        line = content.count('\n', 0, pos) + 1
        col_start = pos - line_start
        col_end = col_start + len(self.find_str.get())
        
        start_index = f"{line}.{col_start}"
        end_index = f"{line}.{col_end}"
        
        self.text_area.tag_remove(tk.SEL, 1.0, tk.END)
        self.text_area.tag_add(tk.SEL, start_index, end_index)
        self.text_area.mark_set(tk.INSERT, end_index)
        self.text_area.see(tk.INSERT)
    
    def replace(self):
        """替换当前选中文本"""
        if not self.text_area.tag_ranges(tk.SEL):
            self.find_next()
            return
            
        selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
        find_text = self.find_str.get()
        
        # 检查是否匹配（考虑大小写）
        if (self.case_sensitive.get() and selected_text == find_text) or \
           (not self.case_sensitive.get() and selected_text.lower() == find_text.lower()):
            self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
            self.text_area.insert(tk.SEL_FIRST, self.replace_str.get())
        
        self.find_next()
    
    def replace_all(self):
        """替换所有匹配项"""
        find_text = self.find_str.get()
        replace_text = self.replace_str.get()
        
        if not find_text:
            return
            
        content = self.text_area.get(1.0, tk.END)
        
        # 根据是否区分大小写进行替换
        if not self.case_sensitive.get():
            content_lower = content.lower()
            find_text_lower = find_text.lower()
            
            # 计算所有匹配位置
            positions = []
            pos = content_lower.find(find_text_lower)
            while pos != -1:
                positions.append(pos)
                pos = content_lower.find(find_text_lower, pos + len(find_text))
            
            # 从后向前替换，避免位置偏移
            for pos in reversed(positions):
                start_index = self.text_area.index(f"1.0+{pos}c")
                end_index = self.text_area.index(f"1.0+{pos+len(find_text)}c")
                self.text_area.delete(start_index, end_index)
                self.text_area.insert(start_index, replace_text)
        else:
            # 直接替换所有出现的地方
            new_content = content.replace(find_text, replace_text)
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, new_content)
        
        messagebox.showinfo("替换", f"已完成 {len(positions) if not self.case_sensitive.get() else content.count(find_text)} 处替换")


# ...existing code...

    def set_format_for_selection(self, **kwargs):
        """
        给选中的文本设置格式，如果没有选中则默认全部设置。
        支持参数如：family, size, weight, slant, underline, overstrike, foreground, background
        """
        try:
            # 判断是否有选中内容
            if self.text_area.tag_ranges(tk.SEL):
                start = self.text_area.index(tk.SEL_FIRST)
                end = self.text_area.index(tk.SEL_LAST)
            else:
                # 没有选中，默认全选
                start = "1.0"
                end = tk.END

            # 允许的字体属性
            font_keys = ["family", "size", "weight", "slant", "underline", "overstrike"]
            font_kwargs = {k: v for k, v in kwargs.items() if k in font_keys}

            # 创建一个新的字体对象，基于当前字体
            current_font = font.Font(font=self.text_area.cget("font"))
            current_font.configure(**font_kwargs)

            # 新建一个唯一tag
            tag_name = f"custom_format_{hash(str(kwargs))}"
            self.text_area.tag_add(tag_name, start, end)
            self.text_area.tag_config(tag_name, font=current_font)

            # 支持颜色设置
            if "foreground" in kwargs:
                self.text_area.tag_config(tag_name, foreground=kwargs["foreground"])
            if "background" in kwargs:
                self.text_area.tag_config(tag_name, background=kwargs["background"])
        except Exception as e:
            messagebox.showerror("错误", f"设置格式失败: {str(e)}")

# ...existing code...

    def choose_font(self):
        """选择字体（作用于选中文本或全部）"""
        font_dialog = font.Font(font=self.current_font)
        chosen = tkfontchooser.askfont(parent=self.root, font=font_dialog)
        if chosen:
            # tkfontchooser 返回 dict 或 tuple，需兼容
            if isinstance(chosen, dict):
                self.set_format_for_selection(
                    family=chosen.get('family', self.current_font.cget('family')),
                    size=chosen.get('size', self.current_font.cget('size')),
                    weight=chosen.get('weight', self.current_font.cget('weight')),
                    slant=chosen.get('slant', self.current_font.cget('slant')),
                    underline=chosen.get('underline', self.current_font.cget('underline')),
                    overstrike=chosen.get('overstrike', self.current_font.cget('overstrike'))
                )
            elif isinstance(chosen, tuple) and len(chosen) >= 6:
                family, size, weight, slant, underline, overstrike = chosen[:6]
                self.set_format_for_selection(
                    family=family,
                    size=size,
                    weight=weight,
                    slant=slant,
                    underline=underline,
                    overstrike=overstrike
                )

    def choose_color(self):
        """选择颜色（作用于选中文本或全部）"""
        color = colorchooser.askcolor(parent=self.root)
        if color[1]:
            self.set_format_for_selection(foreground=color[1])

    def toggle_bold(self):
        """切换粗体（作用于选中文本或全部）"""
        # 判断当前选区是否为粗体
        if self.text_area.tag_ranges(tk.SEL):
            start = self.text_area.index(tk.SEL_FIRST)
            tag_names = self.text_area.tag_names(start)
            is_bold = False
            for tag in tag_names:
                try:
                    tag_font = font.Font(font=self.text_area.tag_cget(tag, "font"))
                    if tag_font.cget("weight") == "bold":
                        is_bold = True
                        break
                except:
                    continue
            new_weight = "normal" if is_bold else "bold"
        else:
            new_weight = "normal" if self.current_font.cget("weight") == "bold" else "bold"
        self.set_format_for_selection(weight=new_weight)

    def toggle_italic(self):
        """切换斜体（作用于选中文本或全部）"""
        if self.text_area.tag_ranges(tk.SEL):
            start = self.text_area.index(tk.SEL_FIRST)
            tag_names = self.text_area.tag_names(start)
            is_italic = False
            for tag in tag_names:
                try:
                    tag_font = font.Font(font=self.text_area.tag_cget(tag, "font"))
                    if tag_font.cget("slant") == "italic":
                        is_italic = True
                        break
                except:
                    continue
            new_slant = "roman" if is_italic else "italic"
        else:
            new_slant = "roman" if self.current_font.cget("slant") == "italic" else "italic"
        self.set_format_for_selection(slant=new_slant)

    def toggle_underline(self):
        """切换下划线（作用于选中文本或全部）"""
        if self.text_area.tag_ranges(tk.SEL):
            start = self.text_area.index(tk.SEL_FIRST)
            tag_names = self.text_area.tag_names(start)
            is_underline = False
            for tag in tag_names:
                try:
                    tag_font = font.Font(font=self.text_area.tag_cget(tag, "font"))
                    if tag_font.cget("underline"):
                        is_underline = True
                        break
                except:
                    continue
            new_underline = not is_underline
        else:
            new_underline = not self.current_font.cget("underline")
        self.set_format_for_selection(underline=new_underline)

    def toggle_strikethrough(self):
        """切换删除线（作用于选中文本或全部）"""
        if self.text_area.tag_ranges(tk.SEL):
            start = self.text_area.index(tk.SEL_FIRST)
            tag_names = self.text_area.tag_names(start)
            is_overstrike = False
            for tag in tag_names:
                try:
                    tag_font = font.Font(font=self.text_area.tag_cget(tag, "font"))
                    if tag_font.cget("overstrike"):
                        is_overstrike = True
                        break
                except:
                    continue
            new_overstrike = not is_overstrike
        else:
            new_overstrike = not self.current_font.cget("overstrike")
        self.set_format_for_selection(overstrike=new_overstrike)

    
    def new_blank_file(self):
        """新建空白文件"""
        if self.check_save():
            self.text_area.delete(1.0, tk.END)
            self.current_file = None
            self.text_modified = False
            self.update_title()
            self.status_bar.config(text="新建空白文件")
            # 重置撤销/重做栈
            self.undo_stack.clear()
            self.redo_stack.clear()
    
    def new_template_file(self):
        """基于当前内容新建模板文件"""
        current_content = self.text_area.get(1.0, tk.END)
        if self.check_save():
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(1.0, current_content)
            self.current_file = None
            self.text_modified = True
            self.update_title()
            self.status_bar.config(text="基于模板创建新文件")
            # 重置撤销/重做栈
            self.undo_stack.clear()
            self.redo_stack.clear()
    
    def open_file(self):
        """打开文件"""
        if self.check_save():
            file_path = filedialog.askopenfilename(
                defaultextension=".txt",
                filetypes=[
                    ("文本文档", "*.txt"), 
                    ("Markdown", "*.md"), 
                    ("Python文件", "*.py"),
                    ("所有文件", "*.*")
                ]
            )
            
            if file_path:
                try:
                    # 检查是否是加密文件
                    if file_path in self.secure_notes:
                        if not self.decrypt_file(file_path):
                            return
                    else:
                        with open(file_path, 'r', encoding='utf-8') as file:
                            content = file.read()
                    
                    self.text_area.delete(1.0, tk.END)
                    self.text_area.insert(1.0, content)
                    self.current_file = file_path
                    self.text_modified = False
                    self.update_title()
                    self.add_to_recent_files(file_path)
                    self.status_bar.config(text=f"已打开: {file_path}")
                    # 重置撤销/重做栈
                    self.undo_stack.clear()
                    self.redo_stack.clear()
                except Exception as e:
                    messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")
    
    def save_file(self):
        """保存文件"""
        if self.current_file:
            try:
                content = self.text_area.get(1.0, tk.END)
                with open(self.current_file, 'w', encoding='utf-8') as file:
                    file.write(content)
                
                self.text_modified = False
                self.update_title()
                self.add_to_recent_files(self.current_file)
                self.status_bar.config(text=f"已保存: {self.current_file}")
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存文件时出错:\n{str(e)}")
                return False
        else:
            return self.save_as_file()
    
    def save_as_file(self):
        """另存为文件"""
        file_path = filedialog.asksaveasfilename(
            defaultextension=".txt",
            filetypes=[
                ("文本文档", "*.txt"), 
                ("Markdown", "*.md"), 
                ("Python文件", "*.py"),
                ("所有文件", "*.*")
            ]
        )
        
        if file_path:
            try:
                content = self.text_area.get(1.0, tk.END)
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write(content)
                
                self.current_file = file_path
                self.text_modified = False
                self.update_title()
                self.add_to_recent_files(file_path)
                self.status_bar.config(text=f"已保存: {file_path}")
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存文件时出错:\n{str(e)}")
                return False
        return False
    
    def check_save(self):
        """检查是否需要保存当前文件"""
        if self.text_modified:
            result = messagebox.askyesnocancel(
                "保存更改", 
                "是否保存对文件的更改?"
            )
            
            if result is None:  # 取消
                return False
            elif result:  # 是
                return self.save_file()
            else:  # 否
                return True
        return True
    
    def on_closing(self):
        """关闭窗口时的回调"""
        if self.check_save():
            self.save_config()
            self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = EnhancedNotepad(root)
    root.mainloop()